package com.googlecode.ddplan;

import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.armedbear.lisp.Interpreter;
import org.armedbear.lisp.LispObject;

public class Action {
	private Method method;

	protected Action(Method method) {
		this.method = method;
	}

	protected LispObject getLisp() throws ParseException {
		LispObject actionLisp = Interpreter.readFromString("NIL").add(Interpreter.readFromString(":action"))
				.add(Interpreter.readFromString(method.getName().toUpperCase()));
		addIfPresent(actionLisp, ":parameters", getParameters());
		addIfPresent(actionLisp, ":precondition", getPrecondition());
		addIfPresent(actionLisp, ":effect", getEffect());
		return actionLisp;
	}

	private void addIfPresent(LispObject lisp, String symbol, LispObject lispToAdd) {
		if (lispToAdd.length() > 0) {
			lisp = lisp.reverse().push(Interpreter.readFromString(symbol)).push(lispToAdd).reverse();
		}
	}

	private LispObject getParameters() {
		Map<Class<?>, List<String>> parametersMap = new HashMap<Class<?>, List<String>>();

		List<String> instanceVariable = new ArrayList<String>();
		instanceVariable.add("?0");
		parametersMap.put(method.getClass(), instanceVariable);

		for (int i = 1; i <= method.getParameterTypes().length; i++) {
			Class<?> paramType = method.getParameterTypes()[i - 1];
			if (parametersMap.containsKey(paramType)) {
				parametersMap.get(paramType).add("?" + i);
			} else {
				List<String> variable = new ArrayList<String>();
				variable.add("?" + i);
				parametersMap.put(method.getClass(), variable);
			}
		}

		LispObject parameterLisp = Interpreter.readFromString("NIL");
		for (Class<?> klass : parametersMap.keySet()) {
			parameterLisp.nreverse();
			for (String parameter : parametersMap.get(klass)) {
				parameterLisp = parameterLisp.push(Interpreter.readFromString(parameter));
			}
			parameterLisp = parameterLisp.push(Interpreter.readFromString("-")).push(
					Interpreter.readFromString(klass.getSimpleName().toUpperCase()));
			parameterLisp.nreverse();
		}

		return parameterLisp;
	}

	private LispObject getPrecondition() throws ParseException {
		return generateLispFromStates(method.getAnnotation(PDDLAction.class).precond());
	}

	private LispObject getEffect() throws ParseException {
		return generateLispFromStates(method.getAnnotation(PDDLAction.class).effects());
	}

	private LispObject generateLispFromStates(String[] states) throws ParseException {
		List<LispObject> statesLisp = new ArrayList<LispObject>();
		for (String state : states) {
			StateAction stateAction = new StateAction(state, method);
			stateAction.parse();
			statesLisp.add(stateAction.getState());
		}

		if (statesLisp.size() == 1) {
			return statesLisp.get(0);
		}

		LispObject stateLisp = Interpreter.readFromString("NIL");
		for (LispObject state : statesLisp) {
			stateLisp = stateLisp.push(state);
		}
		return stateLisp.push(Interpreter.readFromString("AND"));
	}
}
